package com.poizon.infrastructure.lock.core.util;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

import static com.poizon.infrastructure.lock.core.enums.LockConstants.*;

@Slf4j
public abstract class AbstractLock implements ILock {
    protected abstract boolean tryLockImpl(String lockName, Long waitTime, Long timeout, TimeUnit timeUnit);

    protected abstract void unLockImpl(String lockName);

    @Override
    public boolean tryLock(String lockName, Long waitTime, Long timeout, TimeUnit timeUnit) throws InterruptedException {
        return tryLockImpl(lockName, waitTime, timeout, timeUnit);
    }

    @Override
    public void unlock(String lockName) {
        unLockImpl(lockName);
    }


    @SneakyThrows
    @Override
    public void tryLock(String lockName, Long waitTime, Long timeout, TimeUnit timeUnit, Runnable serviceExecutor, Runnable exceptionExecutor) {
        Boolean triedLock = tryLock(
                lockName,
                waitTime,
                timeout,
                timeUnit
        );
        // 阻塞重试
        if (triedLock) {
            // 获取锁成功
            try {
                serviceExecutor.run();
            } catch (Exception e) {
                log.info(">锁业务异常:{}", e.getMessage());
                throw e;
            } finally {
                unlock(lockName);
            }
        } else {
            // 获取锁失败
            exceptionExecutor.run();
        }
    }

    @Override
    public boolean tryLock(String lockName, Long timeout) throws InterruptedException {
        return tryLock(lockName, DEFAULT_WAIT_TIME, timeout, DEFAULT_TIME_UNIT);
    }

    @Override
    public boolean tryLock(String lockName) throws InterruptedException {
        return tryLock(lockName, DEFAULT_WAIT_TIME, DEFAULT_SERVICE_LOCK_TIMEOUT, DEFAULT_TIME_UNIT);
    }

    @Override
    public boolean tryLock(String lockName, Long timeout, TimeUnit timeUnit) throws InterruptedException {
        return tryLock(lockName, DEFAULT_WAIT_TIME, timeout, timeUnit);
    }


    @Override
    public void tryLock(String lockName, Runnable serviceExecutor) throws InterruptedException {
        tryLock(lockName, DEFAULT_SERVICE_LOCK_TIMEOUT, serviceExecutor, () -> {
            throw new RuntimeException("fail to get redis lock");
        });
    }

    @Override
    public void tryLock(String lockName, Long timeout, Runnable serviceExecutor) throws InterruptedException {
        tryLock(lockName, timeout, serviceExecutor, () -> {
            throw new RuntimeException("fail to get redis lock");
        });
    }

    @Override
    public void tryLock(String lockName, Long timeout, Runnable serviceExecutor, Runnable exceptionExecutor) throws InterruptedException {
        tryLock(lockName, DEFAULT_WAIT_TIME, timeout, DEFAULT_TIME_UNIT, serviceExecutor, exceptionExecutor);
    }


    @Override
    public void tryLock(String lockName, Long timeout, TimeUnit timeUnit, Runnable serviceExecutor, Runnable exceptionExecutor) throws InterruptedException {
        tryLock(lockName, DEFAULT_WAIT_TIME, timeout, timeUnit, serviceExecutor, exceptionExecutor);
    }

    @Override
    public void tryLock(String lockName, Runnable serviceExecutor, Runnable exceptionExecutor) throws InterruptedException {
        tryLock(lockName, DEFAULT_WAIT_TIME, DEFAULT_SERVICE_LOCK_TIMEOUT, DEFAULT_TIME_UNIT, serviceExecutor, exceptionExecutor);
    }

}
